#if !defined(UTILITIES_HPP)
#define UTILITIES_HPP

#include <cstdlib>

#include "ObjectList.hpp"

static constexpr float pi = 3.1415926;
// 角度转弧度
static inline float deg2radians(float deg) {
    return deg / 180 * pi;
}
// 随机浮点数[0,1)
static inline float rand_float() {
    return (float)rand() / (RAND_MAX + 1.f);
}
// 一定范围内的随机浮点数，[left, right)
static inline float rand_float(float left, float right) {
    return left + rand_float() * (right - left);
}

// 随机向量
static inline Eigen::Vector3f rand_vec() {
    return Eigen::Vector3f(rand_float(), rand_float(), rand_float());
}

// xyz三值都在[left, right)范围内的随机向量
static inline Eigen::Vector3f rand_vec(float left, float right) {
    return Eigen::Vector3f(rand_float(left, right), rand_float(left, right), rand_float(left, right));
}

// 依据概率返回结果
static bool done(float probability) {
    return rand_float() <= probability;
}

// 反射方向
static Eigen::Vector3f reflect(const Eigen::Vector3f& r, const Eigen::Vector3f& normal) {
    return (r - 2 * r.dot(normal) * normal).normalized();
}

// 折射方向
static Eigen::Vector3f refract(const Eigen::Vector3f& r, const Eigen::Vector3f& normal, float etaiOverEtat) {
    auto cosTheta = fminf((-r).dot(normal), 1.f);
    Eigen::Vector3f rOutPerp = etaiOverEtat * (r + cosTheta * normal);
    Eigen::Vector3f rOutPara = -sqrt(fabsf(1.f - rOutPerp.dot(rOutPerp))) * normal;
    return rOutPara + rOutPerp;
}

// 单位球内的随机向量
static inline Eigen::Vector3f rand_vec_in_sphere() {
    float theta = rand_float(0, 2 * pi);
    float phi = rand_float(0, pi);
    float r = rand_float(0, 1);
    return Eigen::Vector3f(
        sinf(phi) * r * cosf(theta),
        sinf(phi) * r * sinf(theta),
        cosf(phi) * r
    );
}

// 限定x的范围在[min, max]
static inline float clamp(float x, float min, float max) {
    if (x <= min) { return min; }
    else if (x >= max) { return max; }
    else { return x; }
}

// 获取视线发射后返回的颜色
static Eigen::Vector3f cast_ray(const Ray& ray, const ObjectList& scene, int depth = 30) {
    // debug
    // if (depth <= 48) {
    //     // printf("%d\n", depth);
    //     return Eigen::Vector3f(1, 0, 0);
    // }
    if (depth <= 0) {
        return Eigen::Vector3f(0, 0, 0);
    }
    auto payload = scene.hit(ray);
    if (payload) {
        Eigen::Vector3f color(0, 0, 0), attenue;
        Ray out;
        bool scatter = payload->mat->scatter(ray, payload.value(), out, attenue);
        if (scatter) {
            color = cast_ray(out, scene, depth - 1).array() * attenue.array();
        }
        // color += 0.2 * cast_ray(Ray(payload->pos, (rand_vec_in_sphere() + payload->normal).normalized()), scene, depth - 1);
        // color += 0.2 * cast_ray(Ray(payload->pos, (rand_vec_in_sphere() + payload->normal).normalized()), scene, depth - 1);
        // color += 0.2 * cast_ray(Ray(payload->pos, (rand_vec_in_sphere() + payload->normal).normalized()), scene, depth - 1);
        return color;
    }
    float t = (ray.direction().y() + 1) * 0.5;
    return t * Eigen::Vector3f(0.5, 0.7, 1.0) + (1 - t) * Eigen::Vector3f(1, 1, 1);
}

#endif // UTILITIES_HPP